<?php
//派发悬赏

set_time_limit(3600);
error_reporting(E_ALL ^ E_NOTICE);
//定义代码路径
define('APPLICATON_ROOT', dirname(dirname(dirname(__FILE__))) . "/pvn.sys/applications/gameapi");
define('SYS_ROOT', dirname(dirname(dirname(__FILE__))) . "/pvn.sys");
define('CFG_ROOT', dirname(dirname(__FILE__)) . "/configs");
define('CORE_DAEMON', 1);

//加载框架
require(SYS_ROOT . '/core/runtime.php');

$go = new matchoffer;

$go->index();


class matchoffer
{
	private $r;

	function index()
	{
		$this->r = new Redis('localhost', 11544);
		$this->r->connect();
		
		$this->r->select_db(0);
		$this->r->flush();

		$this->sortusers();

		$nposts = daocall("offer", "getnunpostoffers", null);
		if(!$nposts) return;

		$page = 1;
		$pagesize = 200;
		$pagecount = ceil($nposts/$pagesize);

		for($page = 1; $page <= $pagecount; $page ++)
		{
			$posts = daocall("offer", "getunpostoffers", array($page, $pagesize));
			if(!$posts) continue;
			foreach($posts as $post)
			{
				$this->caloffers(&$post);
				$this->post($post);
			}
		}

		$this->r->disconnect();
	}
	
	//分组用户
	function sortusers()
	{
		$recordcount = daocall("admin", "nusers", null);
		if($recordcount)
		{
			$pagesize = 200;
			$pagecount = ceil($recordcount / $pagesize);
			$n = 0;
			for($page = 1; $page <= $pagecount; $page ++)
			{
				$res = daocall("admin", "ulist", array($page, $pagesize));
				if($res)
				{
					foreach($res as $row)
					{
						$uid = $row["uid"];
						$lasttm = intval($row["ltm"]);

						$ext = daocall("user", "getuserext", array($uid));
						if ($ext === false || !is_array($ext)) continue;

						$ext["lv"] = modcall("user", "xp2lv", array($ext["xp"]));
						$ulv = $ext["lv"]; 

						$this->r->zadd("users:lv:{$ulv}", $uid, $ext['lasttm']);
					}
				}
			}
		}
	}

	//派发悬赏，当ptm=0时进入派发程序
	function post($offerrow)
	{
		$uid = $offerrow["uid"];
		$tuid = $offerrow["tuid"];

		//打回重发的
		if(!$offerrow["ctm"]) return;

		//派发3人以内的挑战失败打回加钱发起
		if($offerrow["aperson"] > 0 && $offerrow["aperson"] < 3)
		{
			daocall("offer", "setoffer", array($uid, $tuid, 
				array(
					"ctm" => 0, 
					"ptm" => 0,
					"pcount" => 0,
					"auid" => 0,
					"actm"	=> 0,
					"acount"	=> 0,
					"aperson"	=> 0,
					"apersonlist"	=> "",
			)));
			daocall("user", "setusermsg", array($uid, 20, array("uid" => $uid, "tuid" => $tuid)));
			return;
		}

		//派发3人挑战失败后上榜
		if($offerrow["aperson"] >= 3)
		{
			if(daocall("offer", "deloffer", array($uid, $tuid)))
			{
				daocall("dare", "newdare", array($uid, $tuid, 
					array(
						"price" => $offerrow["price"],
						"gb" => $offerrow["gb"],
						"ctm" => time(),
						"auid" => 0,
						"actm" => 0,
						"acount" => 0,
				)));
			}
			return;
		}

		//派发100次没人接上榜
		if($offerrow["pcount"] >= 100)
		{
			if(daocall("offer", "deloffer", array($uid, $tuid)))
			{
				daocall("dare", "newdare", array($uid, $tuid, 
					array(
						"price" => $offerrow["price"],
						"gb" => $offerrow["gb"],
						"ctm" => time(),
						"auid" => 0,
						"actm" => 0,
						"acount" => 0,
				)));
			}
			return;
		}

		$text = daocall("user", "getuserext", array($tuid));
		if ($text === false || !is_array($text)) return;

		$text["lv"] = modcall("user", "xp2lv", array($text["xp"]));
		$ulv = $text["lv"];

		$ruid = 0;
		$llv = $hlv = $ulv;
		$cplv = 0;
		while(!$ruid)
		{
			$cplv += 5; //系统优先随机分配到与被悬赏人等级差不超过5的在线玩家手中，其次考虑等级差不超过10的在线玩家手中
			if($cplv > 10) break;

			while(!$ruid)
			{
				if($ulv - $llv >= $cplv && $hlv - $ulv >= $cplv) break;
				if($ulv - $llv >= $cplv)
				{
					$slv = $hlv;
					$hlv ++;
				}
				else
				{
					$slv = $llv;
					$llv --;
				}

				$rows = $this->r->zrrange("users:lv:{$slv}", 0, 10);
				if(!$rows) continue;

				$ruid = $rows[array_rand($rows)];
			}
		}

		if($ruid)
		{
			daocall("offer", "setoffer", array($uid, $tuid, 
				array(
					"ptm" => time(), 
					"pcount" => $offerrow["pcount"] + 1,
					"auid"	=> $ruid,
					"actm"	=> 0,
					"acount"	=> 0,
			)));
			daocall("offer", "setusermsg", array($ruid, 10, array("uid" => $uid, "tuid" => $tuid)));
			daocall("offer", "setusermsg", array($uid, 11, array("uid" => $uid, "tuid" => $tuid)));
			daocall("offer", "setusermsg", array($tuid, 12, array("uid" => $uid, "tuid" => $tuid)));
		}
		else
		{
			//未找到匹配的分配对象也记录一次派发
			daocall("offer", "setoffer", array($uid, $tuid, 
				array(
					"ptm" => 0, 
					"pcount" => $offerrow["pcount"] + 1,
					"auid"	=> 0,
					"actm"	=> 0,
					"acount"	=> 0,
			)));
		}
	}

	//处理悬赏记录，以便后面的程序知道是否需要重新派发
	function caloffers(&$offerrow)
	{
		$tm = time();

		//不受理超过30分钟，打回重发
		if($offerrow["auid"] && !$offerrow["actm"] && ($tm - $offerrow["ptm"]) > 30 * 60)
		{
			$offerrow["auid"] = 0;
			$offerrow["ptm"] = 0;
			daocall("offer", "setoffer", array($offerrow["uid"], $offerrow["tuid"], array("auid" => $offerrow["auid"], "ptm" => $offerrow["ptm"])));
			return;
		}

		//受理后，24小时过期
		if($offerrow["auid"] && $offerrow["actm"] && ($tm - $offerrow["ptm"]) > 86400)
		{
			$offerrow["auid"] = 0;
			$offerrow["actm"] = 0;
			$offerrow["ptm"] = 0;
			daocall("offer", "setoffer", array($offerrow["uid"], $offerrow["tuid"], array("auid" => $offerrow["auid"], "actm" => $offerrow["actm"], "ptm" => $offerrow["ptm"])));
			return;
		}
	}
}


class Redis {
    
    var $server;
    var $port;
    var $_sock;
 
    function Redis($host='localhost', $port=6379) {
        $this->host = $host;
        $this->port = $port;
    }
    
    function connect() {
        if ($this->_sock)
            return;
        if ($sock = fsockopen($this->host, $this->port, $errno, $errstr)) {
            $this->_sock = $sock;
            return;
        }
        $msg = "Cannot open socket to {$this->host}:{$this->port}";
        if ($errno || $errmsg)
            $msg .= "," . ($errno ? " error $errno" : "") . ($errmsg ? " $errmsg" : "");
        trigger_error("$msg.", E_USER_ERROR);
    }
    
    function disconnect() {
        if ($this->_sock)
            @fclose($this->_sock);
        $this->_sock = null;
    }

	function &select_db($name) {
        $this->connect();
        $this->_write("SELECT $name\r\n");
        return $this->get_response();
    }

    function &flush($all=false) {
        $this->connect();
        $this->_write($all ? "FLUSHALL\r\n" : "FLUSHDB\r\n");
        return $this->get_response();
    }

	function &zadd($name, $value, $score = 1) {
        $this->connect();
        $this->_write("ZADD $name {$score} $value\r\n");
        return $this->get_response();
    }

	function &zrrange($name, $start, $end, $withscore = false) {
		$withscore = $withscore ? " WITHSCORES" : "";
        $this->connect();
        $this->_write("ZREVRANGE {$name} {$start} {$end}{$withscore}\r\n");
        return $this->get_response();
    }
    
    function &_write($s) {
        while ($s) {
            $i = fwrite($this->_sock, $s);
            if ($i == 0) // || $i == strlen($s))
                break;
            $s = substr($s, $i);
        }
    }
    
    function &_read($len=1024) {
        if ($s = fgets($this->_sock))
            return $s;
        $this->disconnect();
        trigger_error("Cannot read from socket.", E_USER_ERROR);
    }
    
    function &get_response() {
        $data = trim($this->_read());
        $c = $data[0];
        $data = substr($data, 1);
        switch ($c) {
            case '-':
                trigger_error(substr($data, 0, 4) == 'ERR ' ? substr($data, 4) : $data, E_USER_ERROR);
                break;
            case '+':
                return $data;
            case '*':
                $num = (int)$data;
                if ((string)$num != $data)
                    trigger_error("Cannot convert multi-response header '$data' to integer", E_USER_ERROR);
                $result = array();
                for ($i=0; $i<$num; $i++)
                    $result[] =& $this->_get_value();
                return $result;
            default:
                return $this->_get_value($c . $data);
        }
    }
    
    function &_get_value($data=null) {
        if ($data === null)
            $data =& trim($this->_read());
        if ($data == '$-1')
            return null;
        $c = $data[0];
        $data = substr($data, 1);
        $i = strpos($data, '.') !== false ? (int)$data : (float)$data;
        if ((string)$i != $data)
            trigger_error("Cannot convert data '$c$data' to integer", E_USER_ERROR);
        if ($c == ':')
            return $i;
        if ($c != '$')
            trigger_error("Unkown response prefix for '$c$data'", E_USER_ERROR);
        $buffer = '';
        while (true) {
            $data =& $this->_read();
            $i -= strlen($data);
            $buffer .= $data;
            if ($i < 0)
                break;
        }
        return substr($buffer, 0, -2);
    }
    
}
?>